package com.adobe.utils ;

/*
Copyright ( c ) 2011, Adobe Systems Incorporated
All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice, 
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the 
documentation and/or other materials provided with the distribution.

* Neither the name of Adobe Systems Incorporated nor the names of its 
contributors may be used to endorse or promote products derived from 
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE, DATA, OR
PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING
NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// HaXe port - Unfinished
// JLM at Justinfront dot 19 June 2011
// 

// ===========================================================================
//    Imports
// ---------------------------------------------------------------------------
//import flash.display3D.* ;
//TODO: import EReg and resolve utils needed.
import flash.utils.* ;



// ===========================================================================
//    Class
// ---------------------------------------------------------------------------
// used for Match and searching strings.
using EReg ;
class AGALMiniAssembler
{
    // ======================================================================
    //    Properties
    // ----------------------------------------------------------------------
    // AGAL bytes and error buffer 
    private var _agalcode           : ByteArray ;
    private var _error              : String ;
    
    private var debugEnabled        : Bool ;
    
    private static var initialized  : Bool ;
    
    
    
    // ======================================================================
    //    Getters
    // ----------------------------------------------------------------------
    private function get_error():       String
    {
        
        return _error ; 
        
    }
    
    
    private function get_agalcode():    ByteArray
    {
        
        return _agalcode ;
        
    }
    
    
    // ======================================================================
    //    Constructor
    // ----------------------------------------------------------------------
    public function AGALMiniAssembler(  ?debugging : Bool = false  )
    {
        
        _agalcode       = null ;
        _error          = "" ;
        debugEnabled    = false ;
        initialized     = false ;
        
        
        debugEnabled = debugging ;
        if(  !initialized  )
        {
            
            init() ;
            
        }
        
    }
    
    
    // ======================================================================
    //    Methods
    // ----------------------------------------------------------------------
    public function assemble(  mode: String, source: String, ?verbose: Bool = false  ) : ByteArray
    {
        
        //TODO: use haxe timer?
        var start   = getTimer() ;
        var isFrag  = false ;
        _agalcode   = new ByteArray() ;            
        _error      = "" ;
        
        if(  mode == FRAGMENT  )
        {
            
            isFrag = true ;
            
        }
        else if(  mode != VERTEX  )
        {
            
            _error = 'ERROR: mode needs to be "' + FRAGMENT + '" or "' + VERTEX + '" but is "' + mode + '".' ;
            
        }
        agalcode.endian = Endian.LITTLE_ENDIAN ;
        agalcode.writeByte(  0xa0  ) ;                // tag version
        agalcode.writeUnsignedInt(  0x1  ) ;        // AGAL version, big endian, bit pattern will be 0x01000000
        agalcode.writeByte(  0xa1  ) ;                // tag program id
        agalcode.writeByte(  isFrag ? 1 : 0  ) ;    // vertex or fragment
        // TODO: swap in EReg
        var lines   = source.replace(  /[\f\n\r\v]+/g, "\n"  ).split(  "\n"  ) ;
        var nest    = 0 ;
        var nops    = 0 ;
        var i       : Int ;
        var lng     = lines.length ;
        
        for (  i in 0...lng )
        {
            
            if( _error == "" )
            {
                break;
            }
            
            var line = Std.string(  lines[ i ]  ) ;
            
            //TODO: not sure search works in haXe like this?
            // remove comments
            var startcomment = line.search(  "//"  ) ;
            
            if(  startcomment != -1  )
            {
                
                line = line.slice(  0, startcomment  ) ;
                
            }
            
            // grab options
            var optsi       = line.search(  /<.*>/g  ) ;
            var opts        : Array ;
            
            if(  optsi != -1  )
            {
                // TODO: swap in EReg
                opts = line.slice(  optsi  ).match(  /( [\w\.\-\+]+ )/gi  ) ;
                line = line.slice(  0, optsi  ) ;
                
            }
            
            // TODO: swap in EReg
            // find opcode
            var opCode          = line.match(  /^\w{3}/ig  ) ;
            //TODO:
            var opFound         = OPMAP.get( opCode[ 0 ] ) ;
            
            // if debug is enabled, output the opcodes
            if( debugEnabled )
            {
                
                trace( opFound ) ;
                
            }
            
            if( opFound == null )
            {
                
                if( line.length >= 3 )
                {
                    
                    trace( "warning: bad line " + i + ": " + lines[ i ] ) ;
                    
                }
                
                //TODO: remove this ??
                continue ;
                
            }
            
            //TODO: find search ?
            line = line.slice( line.search( opFound.name ) + opFound.name.length ) ;
            
            // nesting check
            if( opFound.flags & OP_DEC_NEST )
            {
                
                nest-- ;
                if( nest < 0 )
                {
                    
                    _error = "error: conditional closes without open." ;
                    break ;
                    
                }
                
            }
            
            if( opFound.flags & OP_INC_NEST )
            {
                
                nest++ ;
                if(  nest > MAX_NESTING  )
                {
                    
                    _error = "error: nesting to deep, maximum allowed is " + MAX_NESTING+"." ;
                    break ;
                    
                }
                
            }
            
            if( ( opFound.flags & OP_FRAG_ONLY ) && !isFrag )
            {
                
                _error = "error: opcode is only allowed in fragment programs." ;
                break ;
                
            }
            
            if( verbose )   trace( "emit opcode=" + opFound ) ;
            
            agalcode.writeUnsignedInt( opFound.emitCode ) ;
            nops++ ;
            
            if( nops > MAX_OPCODES )
            {
                
                _error = "error: too many opcodes. maximum is " + MAX_OPCODES + "." ;
                break ;
                
            }
            
            // get operands, use regexp
            var regs = line.match(  /vc\[( [vof][actps]? )( \d* )?( \.[xyzw]( \+\d{1,3} )? )?\]( \.[xyzw]{1,4} )?|( [vof][actps]? )( \d* )?( \.[xyzw]{1,4} )?/gi  ) ;
            
            if( regs.length != opFound.numRegister )
            {
                
                _error = "error: wrong Float of operands. found " + regs.length + " but expected " + opFound.numRegister + "." ;
                break ;
                
            }
            
            var badreg          = false ;
            var pad             = 64 + 64 + 32 ;
            var regLength       = regs.length ;
            
            for ( j in 0...regLength )
            {
                
                var isRelative      = false ;
                var relreg          = regs[ j ].match(  /\[.*\]/ig  ) ;
                
                if( relreg.length > 0 )
                {
                    
                    regs[ j ] = regs[ j ].replace( relreg[ 0 ], "0" ) ;
                    
                    if( verbose ) trace( "IS REL" ) ;
                    
                    isRelative = true ;
                    
                }
                
                var res             = regs[ j ].match(  /^\b[A-Za-z]{1,2}/ig  ) ;
                var regFound        = REGMAP.get( res[ 0 ] ) ;
                
                // if debug is enabled, output the registers
                if(  debugEnabled  ) trace(  regFound  ) ;
                
                if(  regFound == null  )
                {
                    
                    _error = "error: could not parse operand " + j + " ( " + regs[ j ] + " )." ;
                    badreg = true ;
                    break ;
                    
                }
                
                if( isFrag )
                {
                    
                    if( !( regFound.flags & REG_FRAG ) )
                    {
                        
                        _error = "error: register operand " + j + " ( " + regs[ j ] + " ) only allowed in vertex programs." ;
                        badreg = true ;
                        break ;
                        
                    }
                    
                    if( isRelative )
                    {
                        _error = "error: register operand " + j + " ( " + regs[ j ] + " ) relative adressing not allowed in fragment programs." ;
                        badreg = true ;
                        break ;
                    }
                                
                }
                else
                {
                    
                    if(  !(  regFound.flags & REG_VERT  )  )
                    {
                        
                        _error = "error: register operand "+j+" ( "+regs[j]+" ) only allowed in fragment programs." ;
                        badreg = true ;
                        break ;
                        
                    }
                    
                }
                
                regs[ j ] = regs[ j ].slice( regs[ j ].search( regFound.name ) + regFound.name.length ) ;
                //trace(  "REGNUM: " +regs[j]  ) ;
                
                var idxmatch    = isRelative ? relreg[ 0 ].match(  /\d+/  ) : regs[ j ].match(  /\d+/  ) ;
                var regidx      = 0 ;
                
                if( idxmatch )  regidx = idxmatch[ 0 ] ;
                
                if(  regFound.range < regidx  )
                {
                    
                    _error = "error: register operand " + j + " ( " + regs[ j ] + " ) index exceeds limit of " + ( regFound.range + 1 ) + "." ;
                    badreg = true ;
                    break ;
                    
                }
                
                var regmask             = 0 ;
                var maskmatch           = regs[ j ].match(  /( \.[xyzw]{1,4} )/  ) ;
                var isDest              = ( j == 0 && !( opFound.flags & OP_NO_DEST ) ) ;
                var isSampler           = ( j == 2 && (  opFound.flags & OP_SPECIAL_TEX ) ) ;
                var reltype             = 0 ;
                var relsel              = 0 ;
                var reloffset           = 0 ;
                
                if(  isDest && isRelative  )
                {
                    
                    _error = "error: relative can not be destination" ;    
                    badreg = true ; 
                    break ;                                
                    
                }
                
                if( maskmatch )
                {
                    
                    regmask         = 0 ;
                    var cv          : Int ; 
                    var maskLength  = maskmatch[ 0 ].length ;
                    
                    for ( k = 1...maskLength )
                    {
                        
                        cv = maskmatch[ 0 ].charCodeAt( k ) - "x".charCodeAt( 0 ) ;
                        if(  cv > 2  )  cv = 3 ;
                        if(  isDest  )
                        {
                            
                            if( regmask == null )
                            {
                                
                                regmask = 1 << cv ;
                                
                            }
                            
                        }
                        else
                        {
                            
                            if( regmask == null )
                            {
                                
                                regmask = cv << (  (  k - 1  ) << 1  ) ;
                                
                            }
                            
                        }
                        
                    }
                    
                    if( !isDest )
                    {
                        //TODO: not sure if this line is correct
                        for ( k in k...3 )
                        {
                            
                            if( regmask != null )
                            {
                                
                                regmask = cv << (  (  k - 1  ) << 1  ) ; // repeat last
                                
                            }
                            
                        }
                    }
                }
                else
                {
                    
                    regmask = isDest ? 0xf : 0xe4 ; // id swizzle or mask
                    
                }
                
                if(  isRelative  )
                {
                    
                    var relname         = relreg[ 0 ].match(  /[A-Za-z]{1,2}/ig  ) ;
                    var regFoundRel     = REGMAP.get( relname[ 0 ] ) ;
                    
                    if(  regFoundRel == null  )
                    { 
                        
                        _error = "error: bad index register" ; 
                        badreg = true ; 
                        break ;
                        
                    }
                    
                    reltype = regFoundRel.emitCode ;
                    
                    var selmatch = relreg[ 0 ].match(  /( \.[xyzw]{1,1} )/  ) ;                        
                    
                    if( selmatch.length == 0 )
                    {
                        
                        _error = "error: bad index register select" ; 
                        badreg = true ; 
                        break ; 
                                               
                    }
                    
                    relsel = selmatch[ 0 ].charCodeAt( 1 ) - "x".charCodeAt( 0 ) ;
                    
                    if(  relsel > 2  )
                    {
                        
                        relsel = 3 ; 
                        
                    }
                    
                    var relofs = relreg[ 0 ].match(  /\+\d{1,3}/ig  ) ;
                    
                    if( relofs.length > 0 )
                    { 
                        
                        reloffset = relofs[ 0 ] ;
                        
                    }
                    
                    if(  reloffset < 0 || reloffset > 255  )
                    {
                        
                        _error = "error: index offset "+reloffset+" out of bounds. [0..255]" ; 
                        badreg = true ; 
                        break ;
                        
                    }
                    if(  verbose  )
                    {
                        trace(  "RELATIVE: type=" +
                                reltype +
                                "==" +
                                relname[ 0 ] +
                                " sel=" +
                                relsel +
                                "==" +
                                selmatch[ 0 ] +
                                " idx=" +
                                regidx + 
                                " offset=" +
                                reloffset  
                                ) ;
                        
                    }
                    
                }
                
                if( verbose )   trace( "  emit argcode=" + regFound + "[" + regidx + "][" + regmask + "]" ) ;
                
                if(  isDest  )
                {
                    
                    agalcode.writeShort(  regidx  ) ;
                    agalcode.writeByte(  regmask  ) ;
                    agalcode.writeByte(  regFound.emitCode  ) ;
                    pad -= 32 ; 
                    
                }
                else
                {
                    
                    if( isSampler )
                    {
                        
                        if(  verbose  ) trace(  "  emit sampler"  ) ;
                        var samplerbits     = 5 ; // type 5 
                        var optsLength      = opts.length ;
                        var bias            = 0 ;
                         
                        for( k in 0...optsLength )
                        {
                            
                            if( verbose )     trace(  "    opt: "+opts[k]  ) ;
                            
                            var optfound    = SAMPLEMAP.get( opts[ k ] ) ;
                            
                            if( optfound == null )
                            {
                                // todo check that it's a Float...
                                //trace(  "Warning, unknown sampler option: "+opts[k]  ) ;
                                bias = Float( opts[ k ] ) ; 
                                if(  verbose  ) trace(  "    bias: " + bias  ) ;
                                
                            }
                            else
                            {
                                
                                if( optfound.flag != SAMPLER_SPECIAL_SHIFT )
                                {
                                    
                                    samplerbits = samplerbits & ~(  0xf << optfound.flag  ) ;
                                    
                                }
                                if( samplerbits == null )
                                {
                                    
                                    //TODO: was uint needed here ?
                                    samplerbits = optfound.mask << optfound.flag ;
                                    
                                }
                                
                            }
                            
                        }
                        
                        agalcode.writeShort( regidx ) ;
                        agalcode.writeByte( Std.int( bias*8.0 ) ) ;
                        agalcode.writeByte( 0 ) ;                            
                        agalcode.writeUnsignedInt( samplerbits ) ;
                        
                        if( verbose )     trace(  "    bits: " + (  samplerbits - 5  )  ) ;
                        
                        pad -= 64 ;
                        
                    }
                    else
                    {
                        
                        if( j == 0 )
                        {
                            
                            agalcode.writeUnsignedInt( 0 ) ;
                            pad -= 32 ;
                            
                        }
                        
                        agalcode.writeShort( regidx ) ;
                        agalcode.writeByte( reloffset ) ;
                        agalcode.writeByte( regmask ) ;
                        agalcode.writeByte( regFound.emitCode ) ;
                        agalcode.writeByte( reltype ) ;
                        agalcode.writeShort( isRelative ? (  relsel | (  1 << 15  )  ) : 0  ) ;
                        
                        pad -= 64 ;
                        
                    }
                    
                }
                
            }
            
            // pad unused regs
            j = 0;
            while( j < pad )
            {
                agalcode.writeByte(  0  ) ;
                j += 8 ;
            }
            
            if(  badreg  )  break ;
            
        }
        
        if(  _error != ""  )
        {
            _error += "\n  at line " + i + " " + lines[ i ] ;
            agalcode.length = 0 ;
            trace(  _error  ) ;
        }
        
        // trace the bytecode bytes if debugging is enabled
        if( debugEnabled )
        {
            
            var dbgLine         = "generated bytecode:" ;
            var agalLength      = agalcode.length ;
            
            for( index in 0...agalLength )
            {
                
                if( !( index % 16 ) )
                {
                    dbgLine += "\n" ;
                }
                
                if( !( index % 4 ) )
                {
                    
                    dbgLine += " " ;
                    
                }
                
                var byteStr = agalcode[ index ].toString( 16 ) ;
                
                if(  byteStr.length < 2  ) byteStr = "0" + byteStr ;
                
                dbgLine += byteStr ;
                
            }
            
            trace(  dbgLine  ) ;
            
        }
        
        if(  verbose  ) trace(  "AGALMiniAssembler.assemble time: " + (  (  getTimer() - start  ) / 1000  ) + "s"  ) ;
        
        return agalcode ;
        
    }
    
    static private function init()
    {
        
        initialized  = true ;
        OPMAP        = new Dictionary() ;
        REGMAP       = new Dictionary() ;
        SAMPLEMAP    = new Dictionary() ;
        // Fill the dictionaries with opcodes and registers
        OPMAP.set( MOV, new OpCode(  MOV, 2, 0x00, 0  ) ) ;
        OPMAP.set( ADD, new OpCode(  ADD, 3, 0x01, 0  ) ) ;
        OPMAP.set( SUB, new OpCode(  SUB, 3, 0x02, 0  ) ) ;
        OPMAP.set( MUL, new OpCode(  MUL, 3, 0x03, 0  ) ) ;
        OPMAP.set( DIV, new OpCode(  DIV, 3, 0x04, 0  ) ) ;
        OPMAP.set( RCP, new OpCode(  RCP, 2, 0x05, 0  ) ) ;
        OPMAP.set( MIN, new OpCode(  MIN, 3, 0x06, 0  ) ) ;
        OPMAP.set( MAX, new OpCode(  MAX, 3, 0x07, 0  ) ) ;
        OPMAP.set( FRC, new OpCode(  FRC, 2, 0x08, 0  ) ) ;
        OPMAP.set( SQT, new OpCode(  SQT, 2, 0x09, 0  ) ) ;
        OPMAP.set( RSQ, new OpCode(  RSQ, 2, 0x0a, 0  ) ) ;
        OPMAP.set( POW, new OpCode(  POW, 3, 0x0b, 0  ) ) ;
        OPMAP.set( LOG, new OpCode(  LOG, 2, 0x0c, 0  ) ) ;
        OPMAP.set( EXP, new OpCode(  EXP, 2, 0x0d, 0  ) ) ;
        OPMAP.set( NRM, new OpCode(  NRM, 2, 0x0e, 0  ) ) ;
        OPMAP.set( SIN, new OpCode(  SIN, 2, 0x0f, 0  ) ) ;
        OPMAP.set( COS, new OpCode(  COS, 2, 0x10, 0  ) ) ;
        OPMAP.set( CRS, new OpCode(  CRS, 3, 0x11, 0  ) ) ;
        OPMAP.set( DP3, new OpCode(  DP3, 3, 0x12, 0  ) ) ;
        OPMAP.set( DP4, new OpCode(  DP4, 3, 0x13, 0  ) ) ;
        OPMAP.set( ABS, new OpCode(  ABS, 2, 0x14, 0  ) ) ;
        OPMAP.set( NEG, new OpCode(  NEG, 2, 0x15, 0  ) ) ;
        OPMAP.set( SAT, new OpCode(  SAT, 2, 0x16, 0  ) ) ;
        OPMAP.set( M33, new OpCode(  M33, 3, 0x17, OP_SPECIAL_MATRIX  ) ) ;
        OPMAP.set( M44, new OpCode(  M44, 3, 0x18, OP_SPECIAL_MATRIX  ) ) ;
        OPMAP.set( M34, new OpCode(  M34, 3, 0x19, OP_SPECIAL_MATRIX  ) ) ;
        OPMAP.set( IFZ, new OpCode(  IFZ, 1, 0x1a, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( INZ, new OpCode(  INZ, 1, 0x1b, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( IFE, new OpCode(  IFE, 2, 0x1c, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( INE, new OpCode(  INE, 2, 0x1d, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( IFG, new OpCode(  IFG, 2, 0x1e, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( IFL, new OpCode(  IFL, 2, 0x1f, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( IEG, new OpCode(  IEG, 2, 0x20, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( IEL, new OpCode(  IEL, 2, 0x21, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( ELS, new OpCode(  ELS, 0, 0x22, OP_NO_DEST | OP_INC_NEST | OP_DEC_NEST  ) ) ;
        OPMAP.set( EIF, new OpCode(  EIF, 0, 0x23, OP_NO_DEST | OP_DEC_NEST  ) ) ;
        OPMAP.set( REP, new OpCode(  REP, 1, 0x24, OP_NO_DEST | OP_INC_NEST | OP_SCALAR  ) ) ;
        OPMAP.set( ERP, new OpCode(  ERP, 0, 0x25, OP_NO_DEST | OP_DEC_NEST  ) ) ;
        OPMAP.set( BRK, new OpCode(  BRK, 0, 0x26, OP_NO_DEST  ) ) ;
        OPMAP.set( KIL, new OpCode(  KIL, 1, 0x27, OP_NO_DEST | OP_FRAG_ONLY  ) ) ;
        OPMAP.set( TEX, new OpCode(  TEX, 3, 0x28, OP_FRAG_ONLY | OP_SPECIAL_TEX  ) ) ;
        OPMAP.set( SGE, new OpCode(  SGE, 3, 0x29, 0  ) ) ;
        OPMAP.set( SLT, new OpCode(  SLT, 3, 0x2a, 0  ) ) ;
        OPMAP.set( SGN, new OpCode(  SGN, 2, 0x2b, 0  ) ) ;
        
        REGMAP.set( VA, new Register(  VA,    "vertex attribute",     0x0,    7,      REG_VERT | REG_READ                         ) ) ;
        REGMAP.set( VC, new Register(  VC,    "vertex constant",      0x1,    127,    REG_VERT | REG_READ                         ) ) ;
        REGMAP.set( VT, new Register(  VT,    "vertex temporary",     0x2,    7,      REG_VERT | REG_WRITE | REG_READ             ) ) ;
        REGMAP.set( OP, new Register(  OP,    "vertex output",        0x3,    0,      REG_VERT | REG_WRITE                        ) ) ;
        REGMAP.set( V,  new Register(  V,     "varying",              0x4,    7,      REG_VERT | REG_FRAG | REG_READ | REG_WRITE  ) ) ;
        REGMAP.set( FC, new Register(  FC,    "fragment constant",    0x1,    27,     REG_FRAG | REG_READ                         ) ) ;
        REGMAP.set( FT, new Register(  FT,    "fragment temporary",   0x2,    7,      REG_FRAG | REG_WRITE | REG_READ             ) ) ;
        REGMAP.set( FS, new Register(  FS,    "texture sampler",      0x5,    7,      REG_FRAG | REG_READ                         ) ) ;
        REGMAP.set( OC, new Register(  OC,     "fragment output",     0x3,    0,      REG_FRAG | REG_WRITE                        ) ) ;
        
        SAMPLEMAP.set( D2,          new Sampler(  D2,             SAMPLER_DIM_SHIFT,          0           ) ) ;
        SAMPLEMAP.set( D3,          new Sampler(  D3,             SAMPLER_DIM_SHIFT,          2           ) ) ;
        SAMPLEMAP.set( CUBE,        new Sampler(  CUBE,           SAMPLER_DIM_SHIFT,          1           ) ) ;
        SAMPLEMAP.set( MIPNEAREST,  new Sampler(  MIPNEAREST,     SAMPLER_MIPMAP_SHIFT,       1           ) ) ;
        SAMPLEMAP.set( MIPLINEAR,   new Sampler(  MIPLINEAR,      SAMPLER_MIPMAP_SHIFT,       2           ) ) ;
        SAMPLEMAP.set( MIPNONE,     new Sampler(  MIPNONE,        SAMPLER_MIPMAP_SHIFT,       0           ) ) ;
        SAMPLEMAP.set( NOMIP,       new Sampler(  NOMIP,          SAMPLER_MIPMAP_SHIFT,       0           ) ) ;
        SAMPLEMAP.set( NEAREST,     new Sampler(  NEAREST,        SAMPLER_FILTER_SHIFT,       0           ) ) ;
        SAMPLEMAP.set( LINEAR,      new Sampler(  LINEAR,         SAMPLER_FILTER_SHIFT,       1           ) ) ;
        SAMPLEMAP.set( CENTROID,    new Sampler(  CENTROID,       SAMPLER_SPECIAL_SHIFT,      1 << 0      ) ) ;
        SAMPLEMAP.set( SINGLE,      new Sampler(  SINGLE,         SAMPLER_SPECIAL_SHIFT,      1 << 1      ) ) ;
        SAMPLEMAP.set( DEPTH,       new Sampler(  DEPTH,          SAMPLER_SPECIAL_SHIFT,      1 << 2      ) ) ;
        SAMPLEMAP.set( REPEAT,      new Sampler(  REPEAT,         SAMPLER_REPEAT_SHIFT,       1           ) ) ;
        SAMPLEMAP.set( WRAP,        new Sampler(  WRAP,           SAMPLER_REPEAT_SHIFT,       1           ) ) ;
        SAMPLEMAP.set( CLAMP,       new Sampler(  CLAMP,          SAMPLER_REPEAT_SHIFT,       0           ) ) ;
    }
    
    // ======================================================================
    //    Constants
    // ----------------------------------------------------------------------
    private static var OPMAP                            : Dictionary ;
    private static var REGMAP                           : Dictionary ;
    private static var SAMPLEMAP                        : Dictionary ;
    
    private static inline var MAX_NESTING               : Int               = 4 ;
    private static inline var MAX_OPCODES               : Int               = 256 ;
    
    private static inline var FRAGMENT                  : String            = "fragment" ;
    private static inline var VERTEX                    : String            = "vertex" ;
    
    // masks and shifts
    private static inline var SAMPLER_DIM_SHIFT         : Int               = 12 ;
    private static inline var SAMPLER_SPECIAL_SHIFT     : Int               = 16 ;
    private static inline var SAMPLER_REPEAT_SHIFT      : Int               = 20 ;
    private static inline var SAMPLER_MIPMAP_SHIFT      : Int               = 24 ;
    private static inline var SAMPLER_FILTER_SHIFT      : Int               = 28 ;
    
    // regmap flags
    private static inline var REG_WRITE                 : Int               = 0x1 ;
    private static inline var REG_READ                  : Int               = 0x2 ;
    private static inline var REG_FRAG                  : Int               = 0x20 ;
    private static inline var REG_VERT                  : Int               = 0x40 ;
    
    // opmap flags
    private static inline var OP_SCALAR                 : Int               = 0x1 ;
    private static inline var OP_INC_NEST               : Int               = 0x2 ;
    private static inline var OP_DEC_NEST               : Int               = 0x4 ;
    private static inline var OP_SPECIAL_TEX            : Int               = 0x8 ;
    private static inline var OP_SPECIAL_MATRIX         : Int               = 0x10 ;
    private static inline var OP_FRAG_ONLY              : Int               = 0x20 ;
    private static inline var OP_VERT_ONLY              : Int               = 0x40 ;
    private static inline var OP_NO_DEST                : Int               = 0x80 ;
    
    // opcodes
    private static inline var MOV                       : String            = "mov" ;
    private static inline var ADD                       : String            = "add" ;
    private static inline var SUB                       : String            = "sub" ;
    private static inline var MUL                       : String            = "mul" ;
    private static inline var DIV                       : String            = "div" ;
    private static inline var RCP                       : String            = "rcp" ;
    private static inline var MIN                       : String            = "min" ;
    private static inline var MAX                       : String            = "max" ;
    private static inline var FRC                       : String            = "frc" ;
    private static inline var SQT                       : String            = "sqt" ;
    private static inline var RSQ                       : String            = "rsq" ;
    private static inline var POW                       : String            = "pow" ;
    private static inline var LOG                       : String            = "log" ;
    private static inline var EXP                       : String            = "exp" ;
    private static inline var NRM                       : String            = "nrm" ;
    private static inline var SIN                       : String            = "sin" ;
    private static inline var COS                       : String            = "cos" ;
    private static inline var CRS                       : String            = "crs" ;
    private static inline var DP3                       : String            = "dp3" ;
    private static inline var DP4                       : String            = "dp4" ;
    private static inline var ABS                       : String            = "abs" ;
    private static inline var NEG                       : String            = "neg" ;
    private static inline var SAT                       : String            = "sat" ;
    private static inline var M33                       : String            = "m33" ;
    private static inline var M44                       : String            = "m44" ;
    private static inline var M34                       : String            = "m34" ;
    private static inline var IFZ                       : String            = "ifz" ;
    private static inline var INZ                       : String            = "inz" ;
    private static inline var IFE                       : String            = "ife" ;
    private static inline var INE                       : String            = "ine" ;
    private static inline var IFG                       : String            = "ifg" ;
    private static inline var IFL                       : String            = "ifl" ;
    private static inline var IEG                       : String            = "ieg" ;
    private static inline var IEL                       : String            = "iel" ;
    private static inline var ELS                       : String            = "els" ;
    private static inline var EIF                       : String            = "eif" ;
    private static inline var REP                       : String            = "rep" ;
    private static inline var ERP                       : String            = "erp" ;
    private static inline var BRK                       : String            = "brk" ;
    private static inline var KIL                       : String            = "kil" ;
    private static inline var TEX                       : String            = "tex" ;
    private static inline var SGE                       : String            = "sge" ;
    private static inline var SLT                       : String            = "slt" ;
    private static inline var SGN                       : String            = "sgn" ;
    
    // registers
    private static inline var VA                        : String            = "va" ;
    private static inline var VC                        : String            = "vc" ;
    private static inline var VT                        : String            = "vt" ;
    private static inline var OP                        : String            = "op" ;
    private static inline var V                         : String            = "v" ;
    private static inline var FC                        : String            = "fc" ;
    private static inline var FT                        : String            = "ft" ;
    private static inline var FS                        : String            = "fs" ;
    private static inline var OC                        : String            = "oc" ;
    
    // samplers
    private static inline var D2                        : String            = "2d" ;
    private static inline var D3                        : String            = "3d" ;
    private static inline var CUBE                      : String            = "cube" ;
    private static inline var MIPNEAREST                : String            = "mipnearest" ;
    private static inline var MIPLINEAR                 : String            = "miplinear" ;
    private static inline var MIPNONE                   : String            = "mipnone" ;
    private static inline var NOMIP                     : String            = "nomip" ;
    private static inline var NEAREST                   : String            = "nearest" ;
    private static inline var LINEAR                    : String            = "linear" ;
    private static inline var CENTROID                  : String            = "centroid" ;
    private static inline var SINGLE                    : String            = "single" ;
    private static inline var DEPTH                     : String            = "depth" ;
    private static inline var REPEAT                    : String            = "repeat" ;
    private static inline var WRAP                      : String            = "wrap" ;
    private static inline var CLAMP                     : String            = "clamp" ;
    
    
}
